package com.ruoyi.depot.service.impl;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.enums.InventoryChangeType;
import com.ruoyi.common.enums.InventoryStatus;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.depot.domain.*;
import com.ruoyi.depot.domain.dto.LocalInventoryUpdate;
import com.ruoyi.depot.domain.vo.AddInventoryTaskVO;
import com.ruoyi.depot.domain.vo.LocalInventoryVO;
import com.ruoyi.depot.mapper.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.depot.service.IStocktakesService;
import org.springframework.transaction.annotation.Transactional;

/**
 * 盘点任务Service业务层处理
 *
 * @author luxiaoyu
 * @date 2025-01-06
 */
@Service
public class StocktakesServiceImpl implements IStocktakesService {
	@Autowired
	private StocktakesMapper stocktakesMapper;

	@Autowired
	private StocktakeDetailsMapper stocktakeDetailsMapper;

	@Autowired
	private LocalInventoryMapper localInventoryMapper;

	@Autowired
	private LocalInventoryDetailsMapper localInventoryDetailsMapper;

	@Autowired
	private InboundStockMapper inboundStockMapper;

	@Autowired
	private OutboundStockLogMapper outboundStockLogMapper;

	/**
	 * 查询盘点任务
	 *
	 * @param stocktakeId 盘点任务主键
	 * @return 盘点任务
	 */
	@Override
	public Stocktakes selectStocktakesByStocktakeId(String stocktakeId) {
		if (stocktakeId == null) {
			throw new ServiceException("缺少盘点查询参数!");
		}
		int result = stocktakesMapper.selectCount(stocktakeId);
		if (result <= 0) {
			throw new ServiceException("盘点单不存在,请刷新页面重试!");
		}
		return stocktakesMapper.selectStocktakesByStocktakeId(stocktakeId);
	}

	/**
	 * 查询盘点任务列表
	 *
	 * @param stocktakes 盘点任务
	 * @return 盘点任务
	 */
	@Override
	public List<Stocktakes> selectStocktakesList(Stocktakes stocktakes) {
		return stocktakesMapper.selectStocktakesList(stocktakes);
	}

	/**
	 * 新增盘点任务
	 *
	 * @param addInventoryTaskVO 盘点任务
	 * @return 结果
	 */
	@Transactional
	@Override
	public int insertStocktakes(AddInventoryTaskVO addInventoryTaskVO) {
		addInventoryTaskVO.getProducts().forEach(a -> {
			// 查询盘点产品是否存在于其他正在盘点的盘点单中，如果存在则禁止用户盘点该商品
			List<StocktakeDetails> stocktakeDetails = stocktakeDetailsMapper.selectList(
					new LambdaQueryWrapper<StocktakeDetails>()
							.eq(StocktakeDetails::getSku, a.getSku())
							.ne(StocktakeDetails::getDeleteStatus, 1)  // 排除 delete_status 为 1 的记录
			);
			if (stocktakeDetails.size() > 0) {
				stocktakeDetails.forEach(
						b -> {
							Stocktakes stocktakes = stocktakesMapper.selectByStocktakeId(b.getStocktakeId());
							if (stocktakes != null) {
								if (stocktakes.getStatus() == InventoryStatus.PENDING.getCode() || stocktakes.getStatus() == InventoryStatus.IN_PROGRESS.getCode()) {
									throw new ServiceException("盘点产品SKU为：" + b.getSku() + "的产品已存在于其他未完成的盘点单中,不能再次参与盘点");
								}
							}
						}
				);
			}
		});
		// 添加盘点单
		Stocktakes stocktakes = new Stocktakes();
		stocktakes.setStocktakeId(IdUtils.fastSimpleUUID());
		stocktakes.setStocktakeNumber(generateCode(1).get(0)); // 盘点单号
		stocktakes.setWarehouseId(addInventoryTaskVO.getWarehouseId()); // 仓库id
		stocktakes.setStocktakeDate(new Date()); // 盘点日期
		stocktakes.setCheckedBy(SecurityUtils.getNickname()); // 当前操作人
		stocktakes.setStatus(InventoryStatus.PENDING.getCode()); // 设置盘点状态为待盘点
		stocktakes.setNotes(addInventoryTaskVO.getNotes()); // 备注信息
		stocktakes.setCreatedAt(new Date()); // 创建时间
		stocktakes.setUpdatedAt(new Date()); // 修改时间

		int result = stocktakesMapper.insertStocktakes(stocktakes);

		// 添加盘点单详细
		List<StocktakeDetails> detailsList = addInventoryTaskVO.getProducts().stream().map(item -> {
			StocktakeDetails details = new StocktakeDetails();
			details.setStocktakeDetailId(IdUtils.fastSimpleUUID());
			details.setStocktakeId(stocktakes.getStocktakeId()); // 盘点id
			details.setSku(item.getSku()); // 盘点产品sku
			details.setWarehouseId(addInventoryTaskVO.getWarehouseId()); // 盘点仓库
			details.setSystemQuantity(item.getActualQuantity()); // 系统记录产品库存数量
			details.setNotes(item.getRemark()); // 备注信息
			details.setCountedQuantity(BigDecimal.ZERO); // 实际盘点量初始化0
			details.setDifference(BigDecimal.ZERO); // 初始化差异量为0
			details.setCreatedAt(new Date());
			details.setUpdatedAt(new Date());
			details.setIsInventoried(false); // 默认为未盘点
			details.setInventoryVersion(item.getVersion()); // 库存版本号
			return details;
		}).collect(Collectors.toList());

		// 插入盘点单详细
		stocktakeDetailsMapper.batchInsertStocktakeDetails(detailsList);

		return result;
	}

	private List<String> generateCode(Integer count) {
		synchronized (this) {  // 使用同步块，防止多线程并发访问
			// 获取当前日期
			SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
			String currentDate = dateFormat.format(new Date());

			// 获取最后一个收货单编号，在事务中加锁（使用 FOR UPDATE）
			String lastReceiptNumber = stocktakesMapper.selectLastStocktakeNumberForUpdate(currentDate);

			// 生成的编号列表
			List<String> generatedCodes = new ArrayList<>();

			// 如果没有找到最后一个编号，或者日期不匹配，说明是当天的第一个收货单
			String number;
			if (StringUtils.isEmpty(lastReceiptNumber) || !lastReceiptNumber.substring(2, 10).equals(currentDate)) {
				// 如果没有找到或者日期不匹配，生成从CR202411290001开始
				number = Constants.INBOUND_NUMBER;
			} else {
				// 提取编号中的序列部分，并加一
				String lastNumberStr = lastReceiptNumber.substring(10); // 编号格式：CR + 日期（8位） + 序号（4位）
				long lastNumber = Long.parseLong(lastNumberStr);
				number = String.format("%04d", lastNumber + 1);
			}

			// 生成指定数量的编号
			for (int i = 0; i < count; i++) {
				String code = Constants.STOCKTAKE_NUMBER + currentDate + number;
				// 检查生成的编号是否已存在，避免并发冲突
				int retryCount = 0;
				while (stocktakesMapper.checkStocktakesNumberExists(code) > 0 && retryCount < 5) {
					// 如果编号已经存在，重新生成
					retryCount++;
					String lastReceiptNumberRetry = stocktakesMapper.selectLastStocktakeNumberForUpdate(currentDate);
					if (StringUtils.isEmpty(lastReceiptNumberRetry) || !lastReceiptNumberRetry.substring(2, 10).equals(currentDate)) {
						number = Constants.INBOUND_NUMBER;
					} else {
						String lastNumberStrRetry = lastReceiptNumberRetry.substring(10);
						long lastNumberRetry = Long.parseLong(lastNumberStrRetry);
						number = String.format("%04d", lastNumberRetry + 1);
					}
					code = Constants.STOCKTAKE_NUMBER + currentDate + number;
				}

				if (retryCount >= 5) {
					// 达到重试次数限制，可以抛出异常或返回失败标志
					throw new RuntimeException("Failed to generate unique receipt number after 5 retries.");
				}

				// 将生成的编号添加到列表
				generatedCodes.add(code);

				// 更新编号的序号
				long currentNumber = Long.parseLong(number);
				number = String.format("%04d", currentNumber + 1);
			}

			return generatedCodes;
		}
	}

	/**
	 * 修改盘点任务
	 *
	 * @param stocktakes 盘点任务
	 * @return 结果
	 */
	@Transactional
	@Override
	public int updateStocktakes(Stocktakes stocktakes) {
		// 获取本次盘点的产品列表（仅包含填写了盘点量的产品）
		List<StocktakeDetails> stocktakeDetails = stocktakes.getStocktakeDetails();

		if (stocktakeDetails == null || stocktakeDetails.isEmpty()) {
			throw new IllegalArgumentException("盘点详情不能为空");
		}

		// 拿到所有要进行盘点的产品sku
		List<String> skuList = stocktakeDetails.stream().map(StocktakeDetails::getSku).collect(Collectors.toList());

		// 根据sku查询所有的库存数据，比较对应的版本号是否相同
		Map<String, LocalInventoryVO> skuToInventoryMap = localInventoryMapper.selectSku(skuList, stocktakes.getWarehouseId()).stream()
				.collect(Collectors.toMap(
						LocalInventoryVO::getSku, // 使用 getSku() 的返回值作为键
						inventory -> inventory,   // 使用 LocalInventoryVO 对象本身作为值
						(existing, replacement) -> existing // 如果有重复的键，保留现有的值
				));

		// 作比较，看盘点数据是否已经被改变
		stocktakeDetails.forEach(
				item -> {
					Integer inventoryVersion = item.getInventoryVersion(); // 盘点数据的库存版本号
					Integer version = skuToInventoryMap.get(item.getSku()).getVersion(); // 实时库存版本号
					if(!Objects.equals(inventoryVersion, version)){
						// 说明盘点的库存数据发生了改变，抛出异常提示用户重新进行盘点
						throw new ServiceException("盘点数据" + item.getSku() + "数据发生变化，请新建盘点单进行盘点!");
					}
				}
		);


		// 批量更新盘点单中产品
		stocktakeDetailsMapper.updateBatch(stocktakeDetails);


		// ---开始:处理盘点差异的新逻辑
		for (StocktakeDetails detail : stocktakeDetails) {
			BigDecimal difference = detail.getDifference();  // 差异量
			if (difference == null || difference.compareTo(BigDecimal.ZERO) == 0) {
				continue; // 没有差异或零差异
			}

			String detailSku = detail.getSku();
			Integer detailWarehouseId = detail.getWarehouseId();

			if (difference.compareTo(BigDecimal.ZERO) < 0) {
				//负差异:盘点出库
				int quantityToDeduct = difference.abs().intValue(); // 扣除的绝对数量
				int originalDiscrepancyQty = quantityToDeduct;

				// 查询可扣除的剩余入库日志记录
				List<InboundStock> availableInboundBatches = inboundStockMapper.selectList(
						new LambdaQueryWrapper<InboundStock>()
								.eq(InboundStock::getSku, detailSku)
								.eq(InboundStock::getWarehouseId, detailWarehouseId)
								.gt(InboundStock::getRemainingQuantity, 0)
								.orderByAsc(InboundStock::getReceivedDate) // FIFO
				);

				int actuallyDeductedQuantity = 0; // 累计扣除的数量

				if (availableInboundBatches != null && !availableInboundBatches.isEmpty()) {
					for (InboundStock inboundBatch : availableInboundBatches) {
						if (quantityToDeduct <= 0) break; // 完全扣除

						int deductFromThisBatch = Math.min(quantityToDeduct, inboundBatch.getRemainingQuantity()); // 本次的扣减量

						UpdateWrapper<InboundStock> updateWrapper = new UpdateWrapper<>();
						updateWrapper.eq("id", inboundBatch.getId());
						updateWrapper.set("remaining_quantity", inboundBatch.getRemainingQuantity() - deductFromThisBatch); // 扣减入库日志的剩余库存量
						inboundStockMapper.update(null, updateWrapper);


						BigDecimal costFromThisBatch = BigDecimal.valueOf(deductFromThisBatch)
								.multiply(inboundBatch.getUnitCost()); // 本次的扣减成本

						quantityToDeduct -= deductFromThisBatch; // 累减需要扣除的量
						actuallyDeductedQuantity += deductFromThisBatch; // 累计实际可扣除量



						// 即使从可用库存中仅部分满足或无法满足，也要记录出库操作
						// 'actuallyDeductedQuantity'反映了实际可处理的数量。
						OutboundStockLog outboundLog = new OutboundStockLog();
						outboundLog.setOutboundOrderId(stocktakes.getStocktakeId());
						outboundLog.setBatchId(inboundBatch.getId());
						outboundLog.setDeductedQuantity(deductFromThisBatch); // 记录实际扣除的数量
						outboundLog.setDeductedDate(stocktakes.getStocktakeDate() != null ? stocktakes.getStocktakeDate() : new Date());
						outboundLog.setDeductionCost(costFromThisBatch); // 盘点扣减的成本
						outboundLog.setSku(detailSku); // 产品SKU
						String remark = " 盘点出库：原始差异数量" + originalDiscrepancyQty;
						outboundLog.setRemark(remark);
						outboundStockLogMapper.insert(outboundLog);
					}
				}

				if (actuallyDeductedQuantity < originalDiscrepancyQty) {
					throw new ServiceException("已处理  " + actuallyDeductedQuantity + "。入库批次中的库存不足，无法覆盖全部差异。");
				} else if (availableInboundBatches == null || availableInboundBatches.isEmpty()) {
					throw new ServiceException("未找到SKU " + detailSku + " 在仓库中 " + detailWarehouseId + " 来处理差异.");
				}

			} else {
				// 正差异:库存入库
				int quantityToAdd = difference.intValue();

				// 查找此 SKU 和仓库的最早入库记录
				List<InboundStock> inboundStocksToUpdate = inboundStockMapper.selectList(
						new LambdaQueryWrapper<InboundStock>()
								.eq(InboundStock::getSku, detailSku)
								.eq(InboundStock::getWarehouseId, detailWarehouseId)
								.orderByAsc(InboundStock::getReceivedDate) // FIFO找到最早的
								.last("LIMIT 1") // 获取最早的那一个
				);

				if (inboundStocksToUpdate != null && !inboundStocksToUpdate.isEmpty()) {
					InboundStock earliestInbound = inboundStocksToUpdate.get(0);
					String currentRemark = earliestInbound.getRemark() == null ? "" : earliestInbound.getRemark();
					if (!currentRemark.isEmpty()) {
						currentRemark += "; ";
					}
					currentRemark += "库存入库:差异数量" + quantityToAdd;
					UpdateWrapper<InboundStock> updateWrapper = new UpdateWrapper<>();
					updateWrapper.eq("id", earliestInbound.getId());
					updateWrapper.set("quantity", earliestInbound.getQuantity() + quantityToAdd);
					updateWrapper.set("remaining_quantity", earliestInbound.getRemainingQuantity() + quantityToAdd);
					updateWrapper.set("remark", currentRemark);
					inboundStockMapper.update(null, updateWrapper);
				} else {
					throw new ServiceException("库存盘点入库:未找到 SKU 的现有入库批次 " + detailSku +
							" 在仓库 " + detailWarehouseId + " 应用正差值 " + quantityToAdd);
				}
			}
		}
		// --- 结束:处理库存盘点差异的新逻辑


		// 批量查询库存信息
		List<String> skus = stocktakeDetails.stream()
				.map(StocktakeDetails::getSku)
				.collect(Collectors.toList());
		List<Long> warehouseIds = stocktakeDetails.stream()
				.map(detail -> detail.getWarehouseId().longValue())
				.collect(Collectors.toList());

		List<LocalInventory> inventories = localInventoryMapper.selectProductsBySkusAndWarehouses(skus, warehouseIds);
		Map<String, LocalInventory> inventoryMap = inventories.stream()
				.collect(Collectors.toMap(inv -> inv.getSku() + "_" + inv.getWarehouseId(), inv -> inv));

		// 准备批量更新库存的数据
		List<LocalInventoryUpdate> inventoryUpdates = new ArrayList<>();
		for (StocktakeDetails detail : stocktakeDetails) {
			String key = detail.getSku() + "_" + detail.getWarehouseId();
			LocalInventory localInventory = inventoryMap.get(key);
			if (localInventory != null) {
				inventoryUpdates.add(new LocalInventoryUpdate(
						detail.getCountedQuantity().add(localInventory.getDefectiveQuantity()),
						detail.getSku(),
						detail.getWarehouseId(),
						localInventory.getVersion()
				));

				// 插入库存变动明细
				insertInventoryDetails(
						localInventory.getInventoryId(),
						localInventory.getSku(),
						localInventory.getWarehouseId(),
						detail.getNotes(),
						InventoryChangeType.STOCKTAKING.getCode(),
						detail.getDifference().abs(),
						detail.getSystemQuantity(),
						detail.getCountedQuantity(),
						stocktakes.getStocktakeNumber());
			} else {
				throw new RuntimeException("未找到 SKU: " + detail.getSku() + " 和仓库 ID: " + detail.getWarehouseId() + " 的库存信息");
			}
		}

		// 批量更新库存
		if (!inventoryUpdates.isEmpty()) {
			localInventoryMapper.updateBatch(inventoryUpdates);
		}

		// 判断整个盘点任务是否已完成
		boolean allCounted = stocktakesMapper.countUncountedDetails(stocktakes.getStocktakeId()) == 0;

		// 设置盘点单的状态
		InventoryStatus newStatus = allCounted ? InventoryStatus.COMPLETED : InventoryStatus.IN_PROGRESS;
		stocktakes.setStatus(newStatus.getCode());

//		if (true) {
//			throw new ServiceException("");
//		}
		// 更新盘点单状态
		return stocktakesMapper.updateStocktakes(stocktakes);
	}


	private void insertInventoryDetails(
			String inventoryId,
			String sku,
			Long warehouseId,
			String remark,
			int changeType,
			BigDecimal quantity,
			BigDecimal previousQuantity,
			BigDecimal postChangeQuantity,
			String storageNumber
	) {
		// 这里插入库存变动明细
		LocalInventoryDetails details = new LocalInventoryDetails();
		details.setInventoryDetailId(IdUtils.fastSimpleUUID());
		details.setInventoryId(inventoryId);
		details.setSku(sku);
		details.setWarehouseId(warehouseId);
		details.setOperatorName(SecurityUtils.getNickname()); // 设置操作人为当前操作人
		details.setOperatingTime(new Date()); // 操作时间
		details.setChangeType(changeType); // 变动类型为入库
		details.setQuantity(quantity); // 变动数量
		details.setNotes(remark); // 备注
		details.setPreviousQuantity(previousQuantity); // 变动前的数量
		details.setPostChangeQuantity(postChangeQuantity); // 变动后的数量
		details.setReferenceId(storageNumber); // 关联的入库单
		localInventoryDetailsMapper.insertLocalInventoryDetails(details); // 插入变动记录
	}

	/**
	 * 批量删除盘点任务
	 *
	 * @param stocktakeIds 需要删除的盘点任务主键
	 * @return 结果
	 */
	@Transactional
	@Override
	public int deleteStocktakesByStocktakeIds(String[] stocktakeIds) {
		if (stocktakeIds.length < 1) {
			throw new ServiceException("缺少删除参数，请检查！");
		}

		// 将数组转换为 List
		List<String> stocktakeIdsList = Arrays.asList(stocktakeIds);

		// 删除盘点单
		int result = stocktakesMapper.deleteStocktakesByStocktakeIds(stocktakeIdsList);

		// 删除盘点单明细
		int delete = stocktakeDetailsMapper.delete(new LambdaQueryWrapper<StocktakeDetails>()
				.in(StocktakeDetails::getStocktakeId, stocktakeIdsList));

		return result;
	}


	/**
	 * 删除盘点任务信息
	 *
	 * @param stocktakeId 盘点任务主键
	 * @return 结果
	 */
	@Override
	public int deleteStocktakesByStocktakeId(Long stocktakeId) {
		return stocktakesMapper.deleteStocktakesByStocktakeId(stocktakeId);
	}
}
